// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// Copyright (C) 2012-2013	JoostinOnline

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <ogc/es.h>

#include "debug.h"
#include "grlib/grlib.h"
#include "init.h"
#include "postLoader.h"
#include "settings.h"
#include "sysconf.h"
#include "title.h"

extern bool GetIni(void);

bool SettingsChanged;
bool vWii;
int options[9] = {3, 2, 2, 3, 2, 2, 2, 5, 2};

enum
	{
	RESOLUTION = 0, WIDESCREEN, SENSORBAR, SOUNDMODE, RUMBLE, BURNIN, SHUTDOWN, SENSITIVITY, WC24, LAST_ITEM
	};

static int settings[LAST_ITEM];

static void Message(u32 delay, const char *text, ...) {
	static char mex[1024] = {0};

	if (text != NULL)
		{
		va_list argp;
		va_start(argp, text);
		vsprintf(mex, text, argp);
		va_end(argp);
		}

	grlib_Redraw(); // Call registered redraw function
	grlib_DrawCenteredWindow(mex, 300, 30, 0, NULL);
	grlib_Render();
	sleep(delay);
	#ifdef DOLPHIN
	sleep(delay * 4); // The sleep command doesn't work properly in Dolphin, so you have to give it extra time
	#endif
} // Message

bool SettingsPresent(void) {
	// Returns false if any settings are missing in SYSCONF
	int i;
	int sum = 0;
	for(i = 0; i < 8; i++) {
		sum += settings[i];
	}
	// An error is far larger than any valid option, so sum >= 0 means all the settings are there
	return sum >= 0;
} // SettingsPresent

int Resolution(void) {
	if ((SYSCONF_GetEuRGB60() == 0) && (vWii == false)) return 2;
	return SYSCONF_GetProgressiveScan();
} // Resolution

bool CheckvWii (void) {
	#ifdef DOLPHIN
	return false;
	#endif
	return ( (*(vu32*)(0xCd8005A0) >> 16 ) == 0xCAFE );
} // CheckvWii

// Detect your current settings
void getCurrentSettings(void) {
	vWii = CheckvWii();
	settings[RESOLUTION] = Resolution(); // Interlaced (480i), Progressive (480p), Interlaced (576i)
	settings[WIDESCREEN] = SYSCONF_GetAspectRatio(); // Fullscreen, Widescreen
	settings[SENSORBAR] = SYSCONF_GetSensorBarPosition(); // Bottom, Top
	settings[SOUNDMODE] = SYSCONF_GetSoundMode(); // Mono, Stereo, Surround
	settings[RUMBLE] = SYSCONF_GetPadMotorMode(); // Off, On
	settings[BURNIN] = SYSCONF_GetScreenSaverMode(); // Off, On
	settings[SHUTDOWN] = SYSCONF_GetShutdownMode(); // Full, Idle
	settings[SENSITIVITY] = SYSCONF_GetIRSensitivity() - 1; // Levels 1 (lowest) through 5 (highest)
	settings[WC24] = SYSCONF_GetWiiConnect24(); // Off, On


	Debug("RESOLUTION is set to %i\n", settings[RESOLUTION]);
	Debug("WIDESCREEN is set to %i\n", settings[WIDESCREEN]);
	Debug("SENSORBAR is set to %i\n", settings[SENSORBAR]);
	Debug("SOUNDMODE is set to %i\n", settings[SOUNDMODE]);
	Debug("RUMBLE is set to %i\n", settings[RUMBLE]);
	Debug("BURNIN is set to %i\n", settings[BURNIN]);
	Debug("SHUTDOWN is set to %i\n", settings[SHUTDOWN]);
	Debug("SENSITIVITY is set to %i\n", settings[SENSITIVITY]);
	Debug("WC24 is set to %i\n", settings[WC24]);
	Debug("Running on a %sWii\n", (vWii) ? "v" : "real ");

	return;
} // getCurrentSettings

/*
 If any of the settings are above a valid number, then they will
 be set to 0.  The same applies if the setting is missing from the
 present.cfg file.  It trusts that people at least have the sense
 not to use a negative number, which would hide the setting in the
 app menu.

 I could add a check for negative numbers, but someone might want
 to leave that set to whatever the current one is.
*/
void getPresent(void) {
	if (!present.enabled) {
		Message(3, "There is no valid present.cfg in your ploader folder");
		return;
	}

	Debug("Loading Present");


	/*if (settings[RESOLUTION] >= 0) settings[RESOLUTION] = present.resolution * (present.resolution < opt[RESOLUTION]);
	if (settings[WIDESCREEN] >= 0) settings[WIDESCREEN] = present.widescreen * (present.widescreen < opt[WIDESCREEN]);
	if (settings[SENSORBAR] >= 0) settings[SENSORBAR] = present.sensorbar * (present.sensorbar < opt[SENSORBAR]);
	if (settings[SOUNDMODE] >= 0) settings[SOUNDMODE] = present.soundmode * (present.soundmode < opt[SOUNDMODE]);
	if (settings[RUMBLE] >= 0) settings[RUMBLE] = present.rumble * (present.rumble < opt[RUMBLE]);
	if (settings[BURNIN] >= 0) settings[BURNIN] = present.burnin * (present.burnin < opt[BURNIN]);
	if (settings[SHUTDOWN] >= 0) settings[SHUTDOWN] = present.shutdown * (present.shutdown < opt[SHUTDOWN]);
	if (settings[SENSITIVITY] >= 0) settings[SENSITIVITY] = present.sensitivity * (present.sensitivity < opt[SENSITIVITY]);
	if (settings[WC24] >= 0) settings[WC24] = present.wc24 * (present.wc24 < opt[WC24]);*/

	setPresent(RESOLUTION, present.resolution);
	setPresent(WIDESCREEN, present.widescreen);
	setPresent(SENSORBAR, present.sensorbar);
	setPresent(SOUNDMODE, present.soundmode);
	setPresent(RUMBLE, present.rumble);
	setPresent(BURNIN, present.burnin);
	setPresent(SHUTDOWN, present.shutdown);
	setPresent(SENSITIVITY, present.sensitivity);
	setPresent(WC24, present.wc24);
	SettingsChanged = true;
} // getPresent

void setPresent(int SettingNumber, int PresentValue) {
	// Make sure setting exists in SYSCONF (it's negative if not)
	if (settings[SettingNumber] >= 0)
		// If the value in present.cfg is >= the number of options (options == max+1), set value to 0
		settings[SettingNumber] = PresentValue * (PresentValue < options[SettingNumber]);
}


// Save new settings to the system
static s32 saveChanges(void) {
	if (!SettingsChanged) {
		Debug("No changes were made");
		return 0;
	}
	// First set all the values
	if (settings[RESOLUTION] >= 0) { // More complicated because it involves 2 settings
		if (settings[RESOLUTION] == 2) {
			settings[RESOLUTION] = 0; // Progressive Scan will save as off
			SYSCONF_SetEuRGB60(0); // 576i will save as On
		} else {
			SYSCONF_SetEuRGB60(1); // Otherwise, 576i will save as off (if possible)
		}
		SYSCONF_SetProgressiveScan(settings[RESOLUTION] > 0); // Interlaced (480i), Progressive (480p), Interlaced (576i)
	}
	if (settings[WIDESCREEN] >= 0) SYSCONF_SetAspectRatio(settings[WIDESCREEN]); // Fullscreen, Widescreen
	if (settings[SENSORBAR] >= 0) SYSCONF_SetSensorBarPosition(settings[SENSORBAR]); // Bottom, Top
	if (settings[SOUNDMODE] >= 0) SYSCONF_SetSoundMode(settings[SOUNDMODE]); // Mono, Stereo, Surround
	if (settings[RUMBLE] >= 0) SYSCONF_SetPadMotorMode(settings[RUMBLE]); // Off, On
	if (settings[BURNIN] >= 0) SYSCONF_SetScreenSaverMode(settings[BURNIN]); // Off, On
	if (settings[SHUTDOWN] >= 0) SYSCONF_SetShutdownMode(settings[SHUTDOWN]); // Full, Idle
	if (settings[SENSITIVITY] >= 0) SYSCONF_SetIRSensitivity(settings[SENSITIVITY] + 1); // Levels 1 (lowest) through 5 (highest)
	if (settings[WC24] >= 0) SYSCONF_SetWiiConnect24(settings[WC24]); // Off, On
	
	// Now copy those values to SYSCONF
	SYSCONF_SaveChanges();
	Debug("Changes saved");
	return 1;
} // saveChanges

bool Prompt2Save(void) {
	int item;
	bool goback = false;
	char menu[64] = {0};
	while (true)
	{
		*menu = '\0';
		grlib_menuAddItem(menu, 1, "Save changes");
		grlib_menuAddItem(menu, 2, "Discard changes");
		grlib_menuAddSeparator(menu);
		grlib_menuAddItem(menu, 3, "Back");

		item = grlib_menu(150, "Do you want to save first?", menu);
		if (item > 0) {
			goback = (item == 3);
			break;
		}
	}
	if (item == 1)
	{
		saveChanges();
		Message(1, "Changes saved!");
	}
	return goback;
} // Prompt2Save


void EmuExitPrompt(void) {
	//stfour
	if (pl_IsDirectBackAvail()) {
		Deinit();
		pl_BootPostloader();
	}
	//stfour/

	int item;
	char menu[100] = {0};
	while (true) {
		*menu = '\0';
		grlib_menuAddItem(menu, 1, "Load Homebrew Channel");
		grlib_menuAddItem(menu, 2, "Load Homebrew Filter");
		grlib_menuAddItem(menu, 3, "Load PostLoader Channel");
		grlib_menuAddSeparator(menu);
		grlib_menuAddItem(menu, 4, "Restart Wii");
		grlib_menuAddItem(menu, 5, "Shutdown Wii");

		item = grlib_menu(150, "Select an exit type", menu);
		switch (item) {
			case 1:
				TITLE_LaunchTitle(TITLE_CUSTOM, "4c554c5a"); // HBC v1.1.1 (not really necessary, since it won't install on NEEK)
				TITLE_LaunchTitle(TITLE_CUSTOM, "af1bf516"); // HBC v1.0.7-1.1.0
				TITLE_LaunchTitle(TITLE_CUSTOM, "4a4f4449"); // HBC JODI
				TITLE_LaunchTitle(TITLE_CUSTOM, "48415858"); // HBC HAXX
				VIDEO_SetBlack(false); // Just in case the directory is there, but not the channel
				Message(1, "Homebrew Channel not installed");
				break;
			case 2:
				TITLE_LaunchTitle(TITLE_CUSTOM, "54484246"); // Homebrew Filter THBF
				TITLE_LaunchTitle(TITLE_CUSTOM, "48424630"); // Homebrew Filter HBF0
				VIDEO_SetBlack(false); // Just in case the directory is there, but not the channel
				Message(1, "Homebrew Filter not installed");
				break;
			case 3:
				TITLE_LaunchTitle(TITLE_CUSTOM, "504f5354"); // postLoader Channel POST
				VIDEO_SetBlack(false); // Just in case the directory is there, but not the channel
				Message(1, "PostLoader Channel not installed");
				break;
			case 4:
				Deinit();
				#ifdef DOLPHIN
				// Dolphin will just go to a black screen if you reset, so exiting is the best option
				exit(0);
				#endif
				SYS_ResetSystem(SYS_RESTART,0,0); // Restart Wii
				break;
			case 5:
				Deinit();
				#ifdef DOLPHIN
				// Dolphin will just go to a black screen if you shutdown, so exiting is the best option
				exit(0);
				#endif
				SYS_ResetSystem(SYS_POWEROFF, 0, 0); // Shutdown Wii
				break;
			}
	}
} // EmuExitPrompt

/*
 WiiSettings applies magic word Pune if necessary and will return you to
 the specifed page (or the System Menu if magic word was applied).  If
 page is NULL, the main settings will be loaded.

 For page values, check the /ogc/wiilaunch.h file
*/
void WiiSettings(const char *page) {
	if (SettingsChanged) // Only ask to save if a setting has been changed
		if (Prompt2Save()) return;
	WII_Initialize();
	/*
	Detect Priiloader's Autoboot and Return To settings from loader.ini.
	If they aren't both set to System Menu (and Priiloader is installed),
	use magic word to launch the System Menu instead.
	*/
	if (GetIni()){
		Message(2, "Loading Wii Settings");
	} else {
		Message(2, "Returning to System Menu");
		*(vu32*)0x8132FFFB = 0x50756e65; // magicword "Pune"
		DCFlushRange((void*)0x8132FFFB, 4); // Thanks to entropy for this line
		*(vu32*)0x817FEFF0 = 0x50756e65; // New magicword "Pune"

		Debug("Magic word 'Pune' (0x50756e65) applied");
	}

	Debug("GetIni Passed");
	if (page != NULL) Debug("page is %s", page);
	Deinit();

	// Dolphin will crash if you try to load Wii Settings, so exiting is the only option
	exit(0);

	WII_ReturnToSettingsPage(page);
} // WiiSettings

int Settings(void)
	{
	int item;
	SettingsChanged = false;
	// Names of posible values of each setting
	const char *vresolution[] = {"Interlaced (480i)", "Progressive (480p)", "Interlaced (576i)"};
	const char *vwidescreen[] = {"Standard (4:3)", "Widescreen (16:9)"};
	const char *vsensorbar[] = {"Bottom", "Top"};
	const char *vsoundmode[] = {"Mono", "Stereo", "Surround"};
	const char *vrumble[] = {"Disabled", "Enabled"};
	const char *vburnin[] = {"Disabled", "Enabled"};
	const char *vshutdown[] = {"Full", "Idle"};
	const char *vsensitivity[] = {"Minimum", "Low", "Medium", "High", "Maximum"};
	const char *vwc24[] = {"Disabled", "Enabled"};

	char menu[1024];

	getCurrentSettings();

	while (true)
		{
		*menu = '\0';
		grlib_menuAddItem(menu, 100, "Video Mode: %s", (settings[RESOLUTION] >= 0) ? vresolution[settings[RESOLUTION]] : "Not Set");
		grlib_menuAddItem(menu, 101, "Video Aspect: %s", (settings[WIDESCREEN] >= 0) ? vwidescreen[settings[WIDESCREEN]] : "Not Set");
		grlib_menuAddItem(menu, 102, "Sensor Bar Position: %s", (settings[SENSORBAR] >= 0) ? vsensorbar[settings[SENSORBAR]] : "Not Set");
		grlib_menuAddItem(menu, 103, "Sound Mode: %s", (settings[SOUNDMODE] >= 0) ? vsoundmode[settings[SOUNDMODE]] : "Not Set");
		grlib_menuAddItem(menu, 104, "Controller Rumble: %s", (settings[RUMBLE] >= 0) ? vrumble[settings[RUMBLE]] : "Not Set");
		grlib_menuAddItem(menu, 105, "Burnin Reduction: %s", (settings[BURNIN] >= 0) ? vburnin[settings[BURNIN]] : "Not Set");
		grlib_menuAddItem(menu, 106, "Shutdown Mode: %s", (settings[SHUTDOWN] >= 0) ? vshutdown[settings[SHUTDOWN]] : "Not Set");
		grlib_menuAddItem(menu, 107, "IR Sensitivity Level: %s", (settings[SENSITIVITY] >= 0) ? vsensitivity[settings[SENSITIVITY]] : "Not Set");
		grlib_menuAddItem(menu, 108, "WiiConnect24: %s", (settings[WC24] >= 0) ? vwc24[settings[WC24]] : "Not Set");

		grlib_menuAddSeparator(menu);

		grlib_menuAddItem(menu, 109, "Load stored settings");
		//grlib_menuAddSeparator(menu);

		if (!vWii) {
			// This option should only be available on a real Wii
			grlib_menuAddItem(menu, 110, "Exit to Internet Settings");
			grlib_menuAddSeparator(menu);
		}
		grlib_menuAddItem(menu, 1, "Save changes and exit");
		grlib_menuAddItem(menu, 2, "Exit without saving");

		item = grlib_menu(250,"Select a Setting", menu);

		if (item >= 100) {
			int i = item - 100;
			if (settings[i] >= 0) {
				settings[i] ++;
			} else {
				WiiSettings(NULL);
			}
			if (settings[i] >= options[i])
				settings[i] = 0; // If you go past the last option, loop back to the first
			if ((settings[RESOLUTION] == 1) && (!VIDEO_HaveComponentCable()))
				settings[RESOLUTION] = 2; // If you don't have component cables, skip the 480p option
			if ((settings[RESOLUTION] == 2) && (SYSCONF_GetEuRGB60() < 0))
				settings[RESOLUTION] = 0; // If 576i setting can't be detected, skip the 576i option
			if (i == 9) {
				getPresent();
				if (present.auto_exit) {
					saveChanges();
					return 0;
				}
			}
			if (i == 10) {
				WiiSettings(SETTINGS_INTERNET);
			} else {
				SettingsChanged = true; // If you load System Settings, you will be prompted to save first
			}
		} else {
			break;
		}
	}

	if (item == 1) {
		saveChanges();
		Message(1, "Changes saved!");
	}

	return item;
} // Settings